# -*- coding: utf-8 -*-

TAM_MEM = 128
SP  = 29
BP  = 30

class Arquitetura(object):
    """ Simulando uma arquitetura MIPS big-endian, ou seja os bits mais
        significativos estão em endereços de memória mais baixos """
    
    def __init__(self):
        self.pc_ini = 0
        self.reset()
        self.running = False
        self.SP = 29
        self.BP = 30
    
    def reset(self):
        self.mem    = [0] * TAM_MEM * 1024      # TAM_MEM KB de memória
        self.pc     = 0                         # Program Counter
        self.hi     = 0                         # Registrador HI
        self.lo     = 0                         # Registrador LO
        self.r_regs = [0] * 32                  # R0 - R31
        self.r_regs[SP] = (TAM_MEM * 1024) - 4
        self.r_regs[BP] = self.r_regs[SP]
    
    def set_controle(self, controle):
        self.controle = controle
    
    def set_interface(self, interface):
        """ Faz a ligação com a iterface do projeto """
        self.interface = interface
        
    def get_mem_word(self, offset):
        return self.mem[offset] << 24 | self.mem[offset+1] << 16 | self.mem[offset+2] << 8 | self.mem[offset+3]
    
    def set_mem_word(self, offset, value):
        self.mem[offset  ] = (value & 0xFF000000) >> 24
        self.mem[offset+1] = (value & 0x00FF0000) >> 16
        self.mem[offset+2] = (value & 0x0000FF00) >> 8
        self.mem[offset+3] = (value & 0x000000FF)
    
    def get_mem_byte(self, offset):
        return (self.mem[offset] & 0xFF)
    
    def set_mem_byte(self, offset, value):
        self.mem[offset] = value & 0xFF
    
    def set_pc_inicial(self, end):
        self.pc_ini = end
    
    def exec_ciclo(self):
        # pegar a instrução corrente (fetch)
        instr = self.get_mem_word(self.pc)
        
        # pegar o opcode
        opcode = (instr & 0xFC000000) >> 26
        
        # executar a instrução
        # passar instr como parametro
        print "self.exec_%02X(instr)" % opcode
        prox_instr = eval("self.exec_%02X(instr)" % opcode)
        
        # manter r0 constante = 0
        self.r_regs[0] = 0
        
        # ir para a próxima instrução
        self.pc = prox_instr
        
        self.interface.carrega_tabela_rotulo()

    def exec_00(self, instr):
        
        # ADD $D, $S, $T
        def f_0x20():
            op_s = (instr & 0x3E00000)    >> 21
            op_t = (instr & 0x1F0000)     >> 16
            op_d = (instr & 0xF800)       >> 11
            self.r_regs[op_d] = self.r_regs[op_s] + self.r_regs[op_t]
            return self.pc + 4
        
        # ADDU $D, $S, $T
        def f_0x21():
            op_s = (instr & 0x3E00000)    >> 21
            op_t = (instr & 0x1F0000)     >> 16
            op_d = (instr & 0xF800)       >> 11
            self.r_regs[op_d] = self.r_regs[op_s] + self.r_regs[op_t]
            return self.pc + 4
        
        # AND $D, $S, $T
        def f_0x24():
            op_s = (instr & 0x3E00000)    >> 21
            op_t = (instr & 0x1F0000)     >> 16
            op_d = (instr & 0xF800)       >> 11
            self.r_regs[op_d] = self.r_regs[op_s] & self.r_regs[op_t]
            return self.pc + 4        
        
        # DIV $S, $T
        def f_0x1A():
            op_s = (instr & 0x3E00000)    >> 21
            op_t = (instr & 0x1F0000)     >> 16
            self.lo = self.r_regs[op_s] / self.r_regs[op_t]
            self.hi = self.r_regs[op_s] % self.r_regs[op_t]
            return self.pc + 4 
        
        # DIVU $S, $T
        def f_0x1B():
            op_s = (instr & 0x3E00000)    >> 21
            op_t = (instr & 0x1F0000)     >> 16
            self.lo = self.r_regs[op_s] / self.r_regs[op_t]
            self.hi = self.r_regs[op_s] % self.r_regs[op_t]
            return self.pc + 4 
        
        # JR $s
        def f_0x08():
            op_s = (instr & 0x3E00000)    >> 21
            return self.r_regs[op_s]
        
        # MFHI $d
        def f_0x10():
            op_d = (instr & 0xF800)       >> 11
            self.r_regs[op_d] = self.hi
            return self.pc + 4
            
        # MFLO $d
        def f_0x12():
            op_d = (instr & 0xF800)       >> 11
            self.r_regs[op_d] = self.lo
            return self.pc + 4

        #PUSH $s
        def f_0x13():
            op_d = (instr & 0xF800)       >> 11
            self.set_mem_word(self.r_regs[SP], self.r_regs[op_d])
            self.interface.add_mem(self.r_regs[SP], self.get_mem_word(self.r_regs[SP]))
            self.r_regs[SP] -= 4
            return self.pc + 4     
        
        #POP
        def f_0x14():
            op_d = (instr & 0xF800) >> 11
            self.r_regs[SP] += 4
            self.r_regs[op_d] = self.get_mem_word(self.r_regs[SP])
            return self.pc + 4
            
        # MULT $s, $t
        def f_0x18():
            op_s = (instr & 0x3E00000)    >> 21
            op_t = (instr & 0x1F0000)     >> 16
            self.lo = self.r_regs[op_s] * self.r_regs[op_t]
            return self.pc + 4
        
        # MULTU $s, $t
        def f_0x19():
            op_s = (instr & 0x3E00000)    >> 21
            op_t = (instr & 0x1F0000)     >> 16
            self.lo = self.r_regs[op_s] * self.r_regs[op_t]
            return self.pc + 4
        
        # OR $d, $s, $t
        def f_0x25():
            op_s = (instr & 0x3E00000)    >> 21
            op_t = (instr & 0x1F0000)     >> 16
            op_d = (instr & 0xF800)       >> 11
            self.r_regs[op_d] = self.r_regs[op_s] | self.r_regs[op_t]
            return self.pc + 4            
        
        # SLL $d, $t, h
        def f_0x00():
            op_t = (instr & 0x1F0000)     >> 16
            op_d = (instr & 0xF800)       >> 11
            op_h = (instr & 0x7C0)        >> 6
            self.r_regs[op_d] = self.r_regs[op_t] << op_h
            return self.pc + 4           
        
        # SLLV $d, $t, $s
        def f_0x04():
            op_s = (instr & 0x3E00000)    >> 21
            op_t = (instr & 0x1F0000)     >> 16
            op_d = (instr & 0xF800)       >> 11
            
            print "op_s = %d op_t = %d op_d = %d" % (self.r_regs[op_s],self.r_regs[op_t],self.r_regs[op_d])
            
            self.r_regs[op_d] = self.r_regs[op_t] << self.r_regs[op_s]
            return self.pc + 4            
        
        # SLT $d, $s, $t
        def f_0x2A():
            op_s = (instr & 0x3E00000)    >> 21
            op_t = (instr & 0x1F0000)     >> 16
            op_d = (instr & 0xF800)       >> 11
            if self.r_regs[op_s] < self.r_regs[op_t]:
                self.r_regs[op_d] = 1
            else:
                self.r_regs[op_d] = 0
            return self.pc + 4             
        
        # SLTU $d, $s, $t
        def f_0x2B():
            op_s = (instr & 0x3E00000)    >> 21
            op_t = (instr & 0x1F0000)     >> 16
            op_d = (instr & 0xF800)       >> 11
            if self.r_regs[op_s] < self.r_regs[op_t]:
                self.r_regs[op_d] = 1
            else:
                self.r_regs[op_d] = 0
            return self.pc + 4                  
        
        # SRA $d, $t, h
        def f_0x03():
            op_t = (instr & 0x1F0000)     >> 16
            op_d = (instr & 0xF800)       >> 11
            op_h = (instr & 0x7C0)        >> 6
            self.r_regs[op_d] = self.r_regs[op_t] >> op_h
            return self.pc + 4

        # SRL $d, $t, h
        def f_0x02():
            op_t = (instr & 0x1F0000)     >> 16
            op_d = (instr & 0xF800)       >> 11
            op_h = (instr & 0x7C0)        >> 6
            self.r_regs[op_d] = self.r_regs[op_t] >> op_h
            return self.pc + 4        
        
        # SRLV $d, $t, $s
        def f_0x06():
            op_s = (instr & 0x3E00000)    >> 21
            op_t = (instr & 0x1F0000)     >> 16
            op_d = (instr & 0xF800)       >> 11
            self.r_regs[op_d] = self.r_regs[op_t] >> self.r_regs[op_s]
            return self.pc + 4           
        
        # SUB $D, $S, $T
        def f_0x22():
            op_s = (instr & 0x3E00000)    >> 21
            op_t = (instr & 0x1F0000)     >> 16
            op_d = (instr & 0xF800)       >> 11
            self.r_regs[op_d] = self.r_regs[op_s] - self.r_regs[op_t]
            return self.pc + 4        
        
        # SUBU $D, $S, $T
        def f_0x23():
            op_s = (instr & 0x3E00000)    >> 21
            op_t = (instr & 0x1F0000)     >> 16
            op_d = (instr & 0xF800)       >> 11
            self.r_regs[op_d] = self.r_regs[op_s] - self.r_regs[op_t]
            return self.pc + 4            
        
        # SYSCALL
        def f_0x0C():
            #implementar SYSCALLS aqui...
            
            #$v0 = $2 contém o código da interrupção
            interrupt = self.r_regs[2]
            
            if(interrupt == 1):
                # Imprime o que está em $a0 = $4
                self.controle.add_msg("%d" % self.r_regs[4])
            else:
                self.controle.add_msg("SYSCALL: Interrupção %d não implementada!" % interrupt)
            
            return self.pc + 4
        
        # XOR $D, $S, $T
        def f_0x26():
            op_s = (instr & 0x3E00000)    >> 21
            op_t = (instr & 0x1F0000)     >> 16
            op_d = (instr & 0xF800)       >> 11
            self.r_regs[op_d] = self.r_regs[op_s] ^ self.r_regs[op_t]
            return self.pc + 4          
        
        # Qual função do opcode 00 a executar?
        funct = instr & 0x3F
        print "f_0x%02X()" % funct
        return eval("f_0x%02X()" % funct)
        
    def exec_08(self, instr):
        
        #ADDI $t, $s, imm
        op_s = (instr & 0x3E00000)    >> 21
        op_t = (instr & 0x1F0000)     >> 16
        imm  = (instr & 0xFFFF)
        self.r_regs[op_t] = self.r_regs[op_s] + imm
        return self.pc + 4
    
    def exec_09(self, instr):
        
        #ADDIU $t, $s, imm
        op_s = (instr & 0x3E00000)    >> 21
        op_t = (instr & 0x1F0000)     >> 16
        imm  = (instr & 0xFFFF)
        self.r_regs[op_t] = self.r_regs[op_s] + imm
        return self.pc + 4    
    
    def exec_0C(self, instr):
        #ANDI $t, $s, imm
        op_s = (instr & 0x3E00000)    >> 21
        op_t = (instr & 0x1F0000)     >> 16
        imm  = (instr & 0xFFFF)
        self.r_regs[op_t] = self.r_regs[op_s] & imm
        return self.pc + 4    


    def exec_04(self, instr):
        #BEQ $s, $t, offset
        op_s = (instr & 0x3E00000)    >> 21
        op_t = (instr & 0x1F0000)     >> 16
        offset  = (instr & 0xFFFF)
        
        if(offset & 0x8000):
            offset = -(((~offset) & 0x0000FFFF)+1)
        
        if(offset >= 0):
            offset +=4
            
        if self.r_regs[op_s] == self.r_regs[op_t]:
            return self.pc + (offset * 4)
        else:
            return self.pc + 4            

    def exec_01(self, instr):
        
        #BGEZ $s, offset
        def f_0x01():
            op_s = (instr & 0x3E00000)    >> 21
            offset  = (instr & 0xFFFF)
            
            if(offset & 0x8000):
                offset = -(((~offset) & 0x0000FFFF)+1)
                
            if(offset >= 0):
                offset +=4                
                
            if(self.r_regs[op_s] & 0x80000000):
                #esse número é negativo
                return self.pc + 4
            else:
                return self.pc + (offset << 2)
            
        #BGEZAL $s, offset
        def f_0x11():
            op_s = (instr & 0x3E00000)    >> 21
            offset  = (instr & 0xFFFF)
            
            if(offset & 0x8000):
                offset = -(((~offset) & 0x0000FFFF)+1)
                
            if(offset >= 0):
                offset +=4                
                
            if(self.r_regs[op_s] & 0x80000000):
                #esse número é negativo
                return self.pc + 4
            else:
                self.r_regs[31] = self.pc + 4   #link
                return self.pc + (offset << 2)      
        
        #BLTZ $s, offset
        def f_0x00():
            op_s = (instr & 0x3E00000)    >> 21
            offset  = (instr & 0xFFFF)
            
            if(offset & 0x8000):
                offset = -(((~offset) & 0x0000FFFF)+1)
            
            if(offset >= 0):
                offset +=4                    
            
            if(self.r_regs[op_s] & 0x80000000):
                return self.pc + (offset << 2)
            else:
                return self.pc + 4    
            
        #BLTZAL $s, offset
        def f_0x10():
            op_s = (instr & 0x3E00000)    >> 21
            offset  = (instr & 0xFFFF)
            
            if(offset & 0x8000):
                offset = -(((~offset) & 0x0000FFFF)+1)
            
            if(offset >= 0):
                offset +=4                    
            
            if(self.r_regs[op_s] & 0x80000000):
                self.r_regs[31] = self.pc + 4   #link
                return self.pc + (offset << 2)
            else:
                return self.pc + 4
            
        # Qual função do opcode 01 a executar?
        funct = (instr & 0x1F0000) >> 16
        return eval("f_0x%02X()" % funct)

    #CALL TARGET
    def exec_3E(self, instr):
        target = instr & 0x3FFFFFF;
        self.set_mem_word(self.r_regs[SP], self.pc+4)
        self.interface.add_mem(self.r_regs[SP], self.get_mem_word(self.r_regs[SP]))
        self.r_regs[SP] -= 4
        return (self.pc & 0xF0000000) | (target << 2)
    
    #RET
    def exec_3F(self, instr):
        self.r_regs[SP] += 4
        retorno = self.get_mem_word(self.r_regs[SP])
        return retorno

    def exec_07(self, instr):
        # BGTZ $s, offset
        op_s = (instr & 0x3E00000)    >> 21
        offset  = (instr & 0xFFFF)
        
        if(offset & 0x8000):
            offset = -(((~offset) & 0x0000FFFF)+1)
        
        if(offset >= 0):
            offset +=4                
        
        if self.r_regs[op_s] > 0 and ((~self.r_regs[op_s]) & 0x80000000):
            return self.pc + (offset << 2)
        else:
            return self.pc + 4      

    def exec_06(self, instr):
        # BLEZ $s, offset
        op_s = (instr & 0x3E00000)    >> 21
        offset  = (instr & 0xFFFF)
        
        if(offset & 0x8000):
            offset = -(((~offset) & 0x0000FFFF)+1)        
        
        if(offset >= 0):
            offset +=4                
        
        if(self.r_regs[op_s] & 0x80000000 or self.r_regs[op_s] == 0):
            return self.pc + (offset << 2)
        else:
            return self.pc + 4
        
    def exec_05(self, instr):
        #BNE $s, $t, offset
        op_s = (instr & 0x3E00000)    >> 21
        op_t = (instr & 0x1F0000)     >> 16
        offset  = (instr & 0xFFFF)
        
        if(offset & 0x8000):
            offset = -(((~offset) & 0x0000FFFF)+1)
        
        if(offset >= 0):
            offset +=4                        
        
        if(self.r_regs[op_s] ^ self.r_regs[op_t]):
            return self.pc + (offset << 2)
        else:
            return self.pc + 4             

    def exec_02(self, instr):
        # J target
        target = instr & 0x3FFFFFF;
        return (self.pc & 0xF0000000) | (target << 2)
        
    def exec_03(self, instr):
        # JAL target
        target = instr & 0x3FFFFFF;
        self.r_regs[31] = self.pc + 4   #link
        return (self.pc & 0xF0000000) | (target << 2)    
        
    def exec_20(self, instr):
        # LB $t, offset($s)
        op_s = (instr & 0x3E00000)    >> 21
        op_t = (instr & 0x1F0000)     >> 16
        offset  = (instr & 0xFFFF)
        self.r_regs[op_t] = self.get_mem_byte(self.r_regs[op_s] + offset)
        return self.pc + 4        
        
    def exec_0F(self, instr):
        # LUI $t, imm
        op_t = (instr & 0x1F0000)     >> 16
        imm  = (instr & 0xFFFF)
        self.r_regs[op_t] = (imm << 16)
        return self.pc + 4   
        
    def exec_23(self, instr):
        # LW $t, offset($s)
        op_s = (instr & 0x3E00000)    >> 21
        op_t = (instr & 0x1F0000)     >> 16
        offset  = (instr & 0xFFFF)
        self.r_regs[op_t] = self.get_mem_word(self.r_regs[op_s] + offset)
        return self.pc + 4      
        
    def exec_0D(self, instr):
        #ORI $t, $s, imm
        op_s = (instr & 0x3E00000)    >> 21
        op_t = (instr & 0x1F0000)     >> 16
        imm  = (instr & 0xFFFF)
        self.r_regs[op_t] = self.r_regs[op_s] | imm
        return self.pc + 4            
        
    def exec_28(self, instr):
        #SB $t, offset($s)
        op_s = (instr & 0x3E00000)    >> 21
        op_t = (instr & 0x1F0000)     >> 16
        offset  = (instr & 0xFFFF)
        self.set_mem_byte(self.r_regs[op_s] + offset, self.r_regs[op_t])
        self.interface.add_mem(self.r_regs[op_s] + offset, self.get_mem_word(self.r_regs[op_s] + offset))
        return self.pc + 4
    
    def exec_0A(self, instr):
        #SLTI $t, $s, imm
        op_s = (instr & 0x3E00000)    >> 21
        op_t = (instr & 0x1F0000)     >> 16
        imm  = (instr & 0xFFFF)
        if self.r_regs[op_s] < imm:
            self.r_regs[op_t] = 1
        else:
            self.r_regs[op_t] = 0
        return self.pc + 4        
        
    def exec_0B(self, instr):
        #SLTIU $t, $s, imm
        op_s = (instr & 0x3E00000)    >> 21
        op_t = (instr & 0x1F0000)     >> 16
        imm  = (instr & 0xFFFF)
        if self.r_regs[op_s] < imm:
            self.r_regs[op_t] = 1
        else:
            self.r_regs[op_t] = 0
        return self.pc + 4
    
    def exec_2B(self, instr):
        #SW $t, offset($s)
        op_s = (instr & 0x3E00000)    >> 21
        op_t = (instr & 0x1F0000)     >> 16
        offset  = (instr & 0xFFFF)
        self.set_mem_word(self.r_regs[op_s] + offset, self.r_regs[op_t])
        self.interface.add_mem(self.r_regs[op_s] + offset, self.get_mem_word(self.r_regs[op_s] + offset))
        return self.pc + 4
    
    def exec_0E(self, instr):
        #XORI $t, $s, imm
        op_s = (instr & 0x3E00000)    >> 21
        op_t = (instr & 0x1F0000)     >> 16
        imm  = (instr & 0xFFFF)
        self.r_regs[op_t] = self.r_regs[op_s] ^ imm
        return self.pc + 4         
    